<?php
namespace app\common\controller;
use \Firebase\JWT\JWT;
use Firebase\JWT\ExpiredException;
use Firebase\JWT\SignatureInvalidException;
use http\Exception\InvalidArgumentException;
use http\Exception\UnexpectedValueException;
use think\Cache;
use app\common\exception\TokenException;
use think\Exception;

class Token {

    /**
     * 获取Token
     * @param $uid ?uid=2
     * @return string
     */
    public static function getToken($uid, $time) {
        $key="zdtx";
        $payload =  [
            'iss'=>'tzld.run',
            'iat'=> $time, // 签发时间
            'uid' => $uid,
            'nbf' => $time + 10, //在什么时间之后该jwt才可用
//            'exp' => time() + 7200, //过期时间-2小时
            'exp' => time() + 360000, //过期时间-2小时
        ];

        $jwt = JWT::encode($payload,$key);

        return $jwt;
    }

    /**
     * 解密
     * @return \think\response\Json
     */
    public static function decode() {
        $key="zdtx";
        $jwt = request()->header('token');

        if (empty($jwt)) {
            $ret = ["code"=>201, "msg"=>"Token不能为空","errorCode"=>300];
            throw new TokenException($ret);
        }
        $res['code'] = 200;
        $res['msg'] = "Token正确";

        JWT::$leeway = 60;
        try {

            $decoded = JWT::decode($jwt, $key, ['HS256']);

            $arr = (array)$decoded;
            $res['data'] = $arr;
            $res['data']['new_token']=0;
        } catch (ExpiredException $e) {
            try {

                $new_jwt=Cache::get('token_blacklist:'.$jwt);

                if(!$new_jwt){
                    $ret = ["code"=>201, "msg"=>"Token过期","errorCode"=>300];
                    throw new TokenException($ret);
                }
                // token过期
                sleep(rand(1,5)/100);
                // 刷新token
                $token = Token::getToken($e->getCode(), time());
                // 设置header

                header('token:'.$token);

                $res['data'] = ['uid'=>$e->getCode(), 'new_token'=>$token];
                // TODO：将之前的token加到很黑名单

                // 将旧token存储在redis中,30秒内再次请求是有效的

                Cache::set('token_blacklist:'.$jwt, $token, 15811200);

            } catch (Exception $es) {
                throw new TokenException(["code"=>201,"msg"=>$es->getMessage(),"errorCode"=>300]);
            }
        }catch (SignatureInvalidException $sie){
            $ret = ["code"=>201, "msg"=>"Token过期","errorCode"=>300];
            throw new TokenException($ret);
        }catch (UnexpectedValueException $exception){
            $ret = ["code"=>201, "msg"=>"签名错误","errorCode"=>300];
            throw new TokenException($ret);
        }catch (InvalidArgumentException $exception){
            $ret = ["code"=>201, "msg"=>"签名不能为空","errorCode"=>300];
            throw new TokenException($ret);
        }

        return $res;
    }

    /**
     * 根据旧的Token获取数据
     * @return array
     * @throws TokenException
     */
    public static function olderToken() {
        $jwt = request()->header('Authorization');
        if (empty($jwt)) {
            return ["code"=>300, "msg"=>"Token不能为空"];

        }
        $decoded = JWT::Edecode($jwt, config('token_secret'), ['HS256']);
        $arr = (array)$decoded;
        return $arr;
    }

}